package com.buddy.design.pattern._02singleton;

import org.junit.Test;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingletonPattern {
    /*
     * 单例设计模式：
     *      确保类在任何情况下绝对只有一个实例，并提供一个全局访问点；
     *      隐藏其构造方法；
     *      属于创造性模式。
     *  确保任何情况下都绝对只有一个实例。
     *  ServletContext(单实例多线程)、ServletConfig、ApplicationContext、DBPool
     *      构造方法私有
     *
     *
     * 1、饿汉式单例
     *      在单例内首次加载的时候就创建实例
     *      缺点：
     *          浪费内存空间（可能用不到）；
     *
     * 2.懒汉式单例
     *      被外部调用时才创建实例
     * 3.注册式单例
     *
     * 4.ThreadLocal单例
     *
     */

    @Test
    public void lazySimpleTest() {
        Thread t1 = new Thread(new ExecuteThread());
        Thread t2 = new Thread(new ExecuteThread());
        t1.start();
        t2.start();
//        while (true);
    }

    @Test
    public void lazyInnerClassSingletonTest() {
        //反射攻击
        Class<?> clazz = LazyInnerClassSingleton.class;
        try {
            Constructor c = clazz.getDeclaredConstructor(null);
            c.setAccessible(true);
            Object o1 = c.newInstance();
            Object o2 = LazyInnerClassSingleton.getInstance();
            System.out.println(o1 == o2);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void serializableSingletonTest() {

        //通过反序列化破坏单例 重写resolveObject方法
        HungrySingleton s1 = null;
        HungrySingleton s2 = HungrySingleton.getInstance();
        try (FileOutputStream fos = new FileOutputStream("SerializableSingleton.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos);
             FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            oos.writeObject(s2);
            oos.flush();

            s1 = (HungrySingleton) ois.readObject();
            System.out.println(s1 == s2);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    @Test
    public void enumSingletonTest() {
        EnumSingleton s1 = null;
        EnumSingleton s2 = EnumSingleton.getInstance();
        s2.setData(new Object());
        try (FileOutputStream fos = new FileOutputStream("SerializableSingleton.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos);
             FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            oos.writeObject(s2);
            oos.flush();

            s1 = (EnumSingleton) ois.readObject();
            System.out.println(s1.getData() == s2.getData());

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void enumSingletonRefelectTest() {
        Class clazz = EnumSingleton.class;
        try {
            Constructor c = clazz.getDeclaredConstructor(String.class, int.class);
            c.setAccessible(true);
            EnumSingleton obj = (EnumSingleton) c.newInstance("Tom","1");
            System.out.println(obj);
            //Cannot reflectively create enum objects
            //从JDK层面就为枚举不被序列化和反射破坏保驾护航
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void containerSingletonTest(){
    }

    @Test
    public void ThreadLocalSingletonTest(){
        Thread t1 = new Thread(new ExecuteThread());
        Thread t2 = new Thread(new ExecuteThread());
        t1.start();
        t2.start();
        while (true);

    }

    /********************************************************************/
    class ExecuteThread implements Runnable {

        @Override
        public void run() {
//            LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
//            System.out.println(instance);
            //com.buddy.design.pattern._02singleton.LazySimpleSingleton@41768441
            //com.buddy.design.pattern._02singleton.LazySimpleSingleton@6e44c341

            System.out.println(ThreadLocalSingleton.getInstance());
            System.out.println(ThreadLocalSingleton.getInstance());
            System.out.println(ThreadLocalSingleton.getInstance());
        }
    }

}
